// array.hpp
//
// Auto-expandable array
//
// Written by Sergei Sokov
//

#ifndef _ARRAY_HPP
#define _ARRAY_HPP

#include "errorm.h"

// Auto-expandable array
template <class T> class Array {
   T*  data;
   int cursize;
   int maxsize;
   int growth_size;
   void _CheckMem(void) {
     if(!data) ERROR("ArrExp: Not enough memory (%i elements)",maxsize);
   }
public:
   Array(int mxsz, int gr) { data=0; Resize(mxsz,gr); }
   Array(int mxsz)         { data=0; Resize(mxsz,1 ); }
   Array()                 { data=0; Resize(1,   1 ); }
   ~Array() { if(data) delete[] data; data=0;}

   void Resize(int mxsz, int gr) {
     if(gr<0) gr=0;
     if(mxsz<=0) mxsz=1;
     if(data) delete[] data;
     data = new T[mxsz]; maxsize=mxsz; cursize=0; growth_size = gr;
     _CheckMem();
   }
   void Resize(int mxsz) {
     Resize(mxsz, growth_size);
   }
   void Expand(void) {
     if(growth_size<=0) return;
     maxsize += growth_size;
     T* newdata = new T[maxsize];
     if(!newdata) ERROR("ArrExp(expand): Not enough memory (%i elements)",maxsize);
     for(int i=0; i<cursize; i++)
       newdata[i] = data[i];
     delete[] data;
     data = newdata;
   }

   void Reset(void) { cursize=0; }

   T& operator[ ] (int i) const {
#if defined(PARANOIDAL)
     if((i<0) || (i>=cursize)) {
       ERROR("ArrExp: index is out of range (%i, sz:%i)", i, cursize);
     }
#endif
     return data[i]; 
   }

   Array<T>& operator=(Array<T>& a) {
     delete[ ] data;
     maxsize = a.maxsize;
     cursize = a.cursize;
     growth_size = a.growth_size;
     data = new T[maxsize];
     _CheckMem();
     for(int i=0; i<cursize; i++)
       data[i] = a.data[i];
     return *this;
   }
   int operator==(Array<T>& a) {
     if(this == &a) return 1;
     return 0;
   }

   int NElements(void) { return cursize; }
   int N(void)         { return cursize; }    // synonym for NElements()

   int FindBack(T& elem, int start, int n) {
     if((start<0) || (start>=cursize))
       return -1;
     for(int i=0; i<n; i++)
       if(data[start-i] == elem) return start-i;
     return -1;
   }
   int Find(T& elem, int start, int n) {
     if((start<0) || (start>=cursize))
       return -1;
     for(int i=0; i<n; i++)
       if(data[start+i] == elem) return start+i;
     return -1;
   }
   int Find(T& elem, int n) {
     return Find(elem, 0, n);
   }
   int Find(T& elem) {
     return Find(elem, 0, cursize);
   }

   int Add(T& elem) {
     if(cursize>=maxsize) {
       if(growth_size<=0) ERROR("Add: Too many elements");
       Expand();
     }
     data[cursize++] = elem;
     return cursize-1;
   }
   void Add(Array& a) {
     if(cursize + a.cursize > maxsize) {
       if(growth_size<=0) ERROR("AddA: Too many elements");
       int addSz = cursize + a.cursize - maxsize;
       if(addSz > growth_size) {
         int sv = growth_size;
         growth_size = addSz;
         Expand();
         growth_size = sv;
       }
       else
         Expand();
     }
     for(int i=0; i<a.cursize; i++)
       data[cursize++] = a.data[i];
   }
   void Del(int idx) {
     if( idx<0 || idx>=cursize )
       ERROR("Del: out of range index: %i",idx);
     for(int i=idx; i<cursize-1; i++)
       data[i] = data[i+1];
     cursize--;
   }

   void Ins(int idx, T& elem) {
     Add(elem);
     for(int i=cursize-1; i>idx; i--)
       data[i] = data[i-1];
     data[idx] = elem;
   }

   int AddUnique(T& elem) {
     int idx = Find(elem);
     if(idx>=0) return idx;
     return Add(elem);
   }
};

// Example of out-of-class member function
//template <class T> Array<T>::Array(int n) {
//   data = new T[n];
//   size = n;
//};
// Array<int> x(5);   // Generate an array to store five integers

#endif

/* eof */
